package gotnames.web.st;

import gotnames.Utils;
import gotnames.dm.Guess;
import gotnames.dm.User;
import gotnames.dm.User.Gender;

import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import com.medallia.tiny.Clock;
import com.medallia.tiny.CollUtils;
import com.medallia.tiny.Empty;
import com.medallia.tiny.Func;
import com.medallia.tiny.Funcs;
import com.medallia.tiny.Predicate;

/**
 * Task which generates the form received by {@link CheckGuessTask}. It displays
 * a row of pictures (see {@link #NUM_PEOPLE}) with a set of names below each
 * picture. The user can pick one of the names for each picture before
 * submitting the form.
 */
public class GuessNamesTask extends GotNamesTask {
	
	/** How many people to display */
	public static final int NUM_PEOPLE = 4;
	
	@Output interface Values {
		V<List<GuessWebView>> PEOPLE = v();
	}
	
	PostAction action(final User loggedInUser, DynamicInput dynamicInput, PersistenceManager pm) {
		final Date now = Clock.now();
		
		// Obtain a list of all users in the group, excluding the logged in users
		Collection<User> allUsers = getAllUsersExceptSelf(pm, loggedInUser);

		Query q = pm.newQuery(Guess.class);
		q.setFilter("userKey == " + loggedInUser.getKey());
		Collection<Guess> guesses = Utils.cast(q.execute());
		final Map<Long, Guess> guessByCorrectUser = Funcs.buildMap(guesses, Guess.CORRECT_USER_FUNC);
		
		List<User> candidateUsers = CollUtils.filteredCopy(allUsers, new Predicate<User>() {
			@Override public boolean accept(User u) {
				// Remove users without profile picture
				if (!u.isProfilePicture())
					return false;
				
				// Remove any users which have been guessed 'recently'
				Guess guess = guessByCorrectUser.get(u.getKey());
				if (guess != null && !Guess.guessAllowed(now, guess))
					return false;
				
				return true;
			}
		});
		
		if (candidateUsers.isEmpty())
			return template("noGuesses");
		
		// Select randomly by shuffling the list and then picking from the top
		Collections.shuffle(candidateUsers);
		
		// The options must match the gender of the person being guessed
		final Map<Gender, List<User>> usersByGender = getUsersByGender(allUsers);

		attr(Values.PEOPLE, Funcs.map(Utils.subList(NUM_PEOPLE, candidateUsers), new Func<User, GuessWebView>() {
			private int counter = 0;
			@Override public GuessWebView call(final User u) {
				final int index = counter++;
				return new GuessWebView() {
					@Override public int getIndex() {
						return index;
					}
					@Override public User getUser() {
						return u;
					}
					@Override public List<User> getOptions() {
						// Options are only of the same gender
						List<User> options = Empty.list(usersByGender.get(u.getGender()));
						// Remove to avoid the correct answer appearing twice
						options.remove(u);
						// Shuffle and pick top 5
						Collections.shuffle(options);
						options = Utils.subList(5, options);
						// Add the correct option back and re-shuffle
						options.add(u);
						Collections.shuffle(options);
						
						return options;
					}
				};
			}
		}));
		return null;
	}
	
	private Map<Gender, List<User>> getUsersByGender(Collection<User> users) {
		final Map<Gender, List<User>> usersByGender = Funcs.partition(users, new Func<User, Gender>() {
			@Override public Gender call(User u) {
				return u.getGender();
			}
		});
		for (Gender g : Gender.values())
			if (!usersByGender.containsKey(g))
				usersByGender.put(g, Collections.<User>emptyList());
		return usersByGender;
	}

	private Collection<User> getAllUsersExceptSelf(PersistenceManager pm, User user) {
		Query q = pm.newQuery(User.class);
		q.setFilter("key != " + user.getKey() + " && groupKey == " + user.getGroupKey());
		return Utils.cast(q.execute());
	}
	
	/** Used to render each picture */
	public interface GuessWebView {
		int getIndex();
		User getUser();
		List<User> getOptions();
	}

	@Override public String getPageTitle() { return "Guess names"; }

}
